package com.lianqi.emcpframework.design.support.aggregate;

import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.lianqi.emcpframework.design.domain.aggregate.Aggregate;
import com.lianqi.emcpframework.design.domain.event.DomainEvent;
import com.lianqi.emcpframework.design.support.AbstractEntity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

/**
 * @program emcp-framework
 * @ClassName AbstractAggregate
 * @description: 聚合根抽象类
 * @author: sky
 * @create: 2019/06/09 21:04
 */
public abstract class AbstractAggregate<T, ID> extends AbstractEntity<T, ID> implements Aggregate<T, ID> {

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAggregate.class);

    @JsonIgnore
    private final transient List<DomainEventItem> eventItems = Lists.newArrayList();

    protected void registerEvent(DomainEvent event) {
        eventItems.add(new DomainEventItem(event));
    }

    protected void registerEvent(Supplier<DomainEvent> supplier) {
        eventItems.add(new DomainEventItem(supplier));
    }

    @JsonIgnore
    @Override
    public List<DomainEvent> getEvents() {
        return Collections
            .unmodifiableList(eventItems.stream().map(item -> item.getEvent()).collect(Collectors.toList()));
    }

    @Override
    public void cleanEvents() {
        eventItems.clear();
    }

    private class DomainEventItem {

        /**
         * 领域事件
         */
        private DomainEvent event;

        /**
         * 领域事件提供者
         */
        private Supplier<DomainEvent> eventSupplier;

        /**
         * 构造方法
         * 
         * @param event
         *            领域事件
         */
        DomainEventItem(DomainEvent event) {
            Preconditions.checkArgument(event != null);
            this.event = event;
        }

        /**
         * 构造方法
         * 
         * @param supplier
         *            领域事件提供
         */
        DomainEventItem(Supplier<DomainEvent> supplier) {
            Preconditions.checkArgument(supplier != null);
            this.eventSupplier = supplier;
        }

        /**
         * 获取领域事件
         * 
         * @return
         */
        public DomainEvent getEvent() {
            if (event != null) {
                return event;
            }
            DomainEvent domainEvent = this.eventSupplier == null ? null : this.eventSupplier.get();
            event = domainEvent;
            return event;
        }
    }
}
